---
title: Agents overview
description: An overview of agents
---

In the Multi-Agent Orchestrator, an agent is a fundamental building block designed to process user requests and generate a response. The `Agent` abstract class serves as the foundation for all specific agent implementations, providing a common structure and interface.

## Agent selection process

The Multi-Agent Orchestrator uses a [Classifier](/multi-agent-orchestrator/classifiers/overview), typically an LLM, to select the most appropriate agent for each user request.

At the heart of this process are the **agent descriptions**.
These descriptions are critical and should be as detailed and comprehensive as possible.

A well-crafted agent description:

- Clearly outlines the agent's capabilities and expertise
- Provides specific examples of tasks it can handle
- Distinguishes it from other agents in the system

The more detailed and precise these descriptions are, the more accurately the Classifier can route requests to the right agent. This is especially important in complex systems with multiple specialized agents.

For a more detailed explanation of the agent selection process, please refer to the [How it works section](/multi-agent-orchestrator/general/how-it-works) section in our documentation.

To optimize agent selection:
- Invest time in crafting thorough, specific agent descriptions
- Regularly review and refine these descriptions
- Use the framework's [agent overlap analysis](/multi-agent-orchestrator/advanced-features/agent-overlap) to ensure clear differentiation between agents

By prioritizing detailed agent descriptions and fine-tuning the selection process, you can significantly enhance the efficiency and accuracy of your Multi-Agent Orchestrator implementation.

## The Agent Abstract Class

The `Agent` class is an abstract base class that defines the essential properties and methods that all agents in the system must have. It's designed to be flexible, allowing for a wide range of implementations from simple API callers to complex LLM-powered conversational agents.

### Key Properties

- `name`: A string representing the name of the agent.
- `id`: A unique identifier for the agent, automatically generated from the name.
- `description`: A string describing the agent's capabilities and expertise.
- `save_chat`: A boolean indicating whether to save the chat history for this agent.
- `callbacks`: An optional `AgentCallbacks` object for handling events like new tokens in streaming responses.

### Abstract Method: process_request

The core functionality of any agent is encapsulated in the `process_request` method. This method must be implemented by all concrete agent classes:

import { Tabs, TabItem } from '@astrojs/starlight/components';

<Tabs syncKey="runtime">
  <TabItem label="TypeScript" icon="seti:typescript" color="blue">
    ```typescript
    abstract processRequest(
      inputText: string,
      userId: string,
      sessionId: string,
      chatHistory: Message[],
      additionalParams?: Record<string, any>
    ): Promise<Message | AsyncIterable<any>>;
    ```
  </TabItem>
  <TabItem label="Python" icon="seti:python">
    ```python
    from abc import abstractmethod
    from typing import Union, AsyncIterable, Optional, Dict, List
    from multi_agent_orchestrator.types import ConversationMessage

    class Agent(ABC):
        @abstractmethod
        async def process_request(
            self,
            input_text: str,
            user_id: str,
            session_id: str,
            chat_history: List[ConversationMessage],
            additional_params: Optional[Dict[str, str]] = None
        ) -> Union[ConversationMessage, AsyncIterable[any]]:
            pass
    ```
  </TabItem>
</Tabs>

- `input_text`: The user's input or query.
- `user_id`: A unique identifier for the user.
- `session_id`: An identifier for the current conversation session.
- `chat_history`: A list of previous messages in the conversation.
- `additional_params`: Optional parameters for additional context or configuration. This is a powerful feature that allows for dynamic customization of agent behavior
  - It's an optional dictionary of key-value pairs that can be passed when calling `route_request` on the orchestrator.
  - These parameters are then forwarded to the appropriate agent's `process_request` method.
  - Custom agents can use these parameters to adjust their behavior or provide additional context for processing the request.

The method returns either a `ConversationMessage` for single responses or an `AsyncIterable` for streaming responses.

Example usage:

<Tabs syncKey="runtime">
  <TabItem label="TypeScript" icon="seti:typescript" color="blue">
    ```typescript
    // When calling routeRequest
    const response = await orchestrator.routeRequest(
      userInput,
      userId,
      sessionId,
      { location: "New York", units: "metric" }
    );

    // In a custom agent's processRequest method
    class WeatherAgent extends Agent {
      async processRequest(
        inputText: string,
        userId: string,
        sessionId: string,
        chatHistory: Message[],
        additionalParams?: Record<string, any>
      ): Promise<Message> {
        const location = additionalParams?.location || "default location";
        const units = additionalParams?.units || "metric";
        // Use location and units to fetch weather data
        // ...
      }
    }
    ```
  </TabItem>
  <TabItem label="Python" icon="seti:python">
    ```python
    # When calling route_request
    response = await orchestrator.route_request(
        user_input,
        user_id,
        session_id,
        additional_params={"location": "New York", "units": "metric"}
    )

    # In a custom agent's process_request method
    class WeatherAgent(Agent):
        async def process_request(
            self,
            input_text: str,
            user_id: str,
            session_id: str,
            chat_history: List[ConversationMessage],
            additional_params: Optional[Dict[str, str]] = None
        ) -> ConversationMessage:
            location = additional_params.get('location', 'default location')
            units = additional_params.get('units', 'metric')
            # Use location and units to fetch weather data
            # ...
    ```
  </TabItem>
</Tabs>

### Agent Options

When creating a new agent, you can specify various options using the `AgentOptions` class:

<Tabs syncKey="runtime">
  <TabItem label="TypeScript" icon="seti:typescript" color="blue">
    ```typescript
    interface AgentOptions {
      name: string;
      description: string;
      modelId?: string;
      region?: string;
      saveChat?: boolean;
      callbacks?: AgentCallbacks;
    }
    ```
  </TabItem>
  <TabItem label="Python" icon="seti:python">
    ```python
    @dataclass
    class AgentOptions:
        name: str
        description: str
        model_id: Optional[str] = None
        region: Optional[str] = None
        save_chat: bool = True
        callbacks: Optional[AgentCallbacks] = None
    ```
  </TabItem>
</Tabs>

### Direct Agent Usage

When you have a single agent use case, you can bypass the orchestrator and call the agent directly. This approach leverages the power of the Multi Agent Orchestrator framework while focusing on a single agent scenario:

<Tabs syncKey="runtime">
  <TabItem label="TypeScript" icon="seti:typescript" color="blue">
    ```typescript
    // Initialize the agent
    const agent = new BedrockLLMAgent({
      name: "custom-agent",
      description: "Handles specific tasks"
    });

    // Call the agent directly
    const response = await agent.agentProcessRequest(
      userInput,
      userId,
      sessionId,
      chatHistory,
      { param1: "value1" }
    );
    ```
  </TabItem>
  <TabItem label="Python" icon="seti:python">
    ```python
    # Initialize the agent
    agent = BedrockLLMAgent(
        name="custom-agent",
        description="Handles specific tasks"
    )

    # Call the agent directly
    response = await agent.agent_process_request(
        input_text=user_input,
        user_id=user_id,
        session_id=session_id,
        chat_history=chat_history,
        additional_params={"param1": "value1"}
    )
    ```
  </TabItem>
</Tabs>

This approach is useful for single agent scenarios where you don't need orchestration but want to leverage the powerful capabilities of the Multi Agent Orchestrator framework.

These options allow you to customize various aspects of the agent's behavior and configuration.